(ns pe.digits)

(defn- digits-base [x base]
  (loop [n x digs '() num 0]
    (cond
      (< n 1) digs
      (< n base) (recur (quot n base) (conj digs (bigint n)) (inc num))
      :else (recur (bigint (quot n base)) (conj digs (rem (bigint n) base)) (inc num)))))

(defn digits [x] (digits-base x 10))

(defn binary-digits [x] (digits-base x 2))

(defn revnum [num]
  (loop [n num acc 0]
    (cond
      (<= n 0) acc
      :else (recur (bigint (quot n 10)) (+ (* 10 acc) (rem n 10))))))

(defn divides? [a x]
  (zero? (rem a x)))

(defn cc [ab]
  (let [[x y] ab]
    (if (= x y) [x] ab)))

(defn divisors [a]
  (let [divs (mapcat cc (for [x (range 1 (inc (Math/sqrt a))) :when (divides? a x)] [x (quot a x)]))]
      divs))

(defn prop-divisors [a]
  (let [divs (mapcat cc (for [x (range 2 (inc (int (Math/sqrt a)))) :when (divides? a x)]
    [x (quot a x)]))]
      (conj divs 1)))

(defn perfect? [n]
  "Returns true if n is a perfect number (when sum of its proper divisors equals to n itself)."
  (= n (reduce + (prop-divisors n))))

(defn deficient? [n]
  "Returns true if n is a deficient number (when sum of its proper divisors is less than n itself)."
  (< (reduce + (prop-divisors n)) n))

(defn abundant? [n]
  "Returns true if n is a abundant number (when sum of its proper divisors is greater than n itself)."
  (> (reduce + (prop-divisors n)) n))

(defn palind?
  "Returns true if x is a palindromic number in the given base (or base 10 by default)"
  ([x] (palind? x 10))
  ([x base] (let [digs (digits-base x base)] (= digs (reverse digs)))))

(defn pandigital? [n to]
  "Return true if n is a pandigital number - i.e. is composed only of digits from 1 to 'to', with one digit of each kind"
  (let [digs (digits n) filtered (filter #(and (> % 0) (<= % to)) (distinct digs))]
    (= (count digs) (count filtered) to)))